package com.start.common.dataRule;

import com.start.common.entity.StartBaseMenuDataRules;
import com.start.common.exceptions.StartBusException;
import com.start.common.tools.ClassTools;
import com.start.common.tools.SpringContextTools;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class EntityQuery<T> extends AbstractCommonQuery<T> {

    /**
     * 对象指端缓存
     */
    private static final Map<String, List<String>> objectCache = new ConcurrentHashMap<>();

    private final Class<T> entityClazz;

    private Map<String, String[]> requestParameterMap = null;
    private List<String> fields = new ArrayList<>();

    @SuppressWarnings("unchecked")
    public EntityQuery(T entity) {
        super(SpringContextTools.getHttpServletRequest());
        if (entity == null) throw new RuntimeException("T entity not null");
        entityClazz = (Class<T>) entity.getClass();
        init();
    }

    public EntityQuery(Class<T> clazz) {
        super(SpringContextTools.getHttpServletRequest());
        this.entityClazz = clazz;
        init();
    }

    private void init() {
        requestParameterMap = request.getParameterMap();
        fields = getObjectFieldMap(entityClazz);
    }

    /**
     * 检查数据规则合法性
     *
     * @param list
     */
    public void checkLegitimacy(List<StartBaseMenuDataRules> list) {
        if (list == null || list.isEmpty()) return;
        List<String> columns = list.stream().map(StartBaseMenuDataRules::getRuleColumnName).collect(Collectors.toList());
        List<String> notLegitimacy = checkLegitimacyColumns(columns);
        if (notLegitimacy.isEmpty()) return;
        throw new StartBusException("数据规则异常：检测到有不合法列名：" + String.join(",", notLegitimacy));
    }


    /**
     * 解析实体查询条件
     */
    @Override
    public void analysisSearchQuery(String prefix) {
        for (String field : fields) {
            String key = prefix + field;
            if (!requestParameterMap.containsKey(key)) continue;
            String[] values = requestParameterMap.get(key);
            if (values.length < 1) continue;
            analysisEntityFieldCondition(field, values);
        }
    }

    @Override
    public void analysisQueryOrder(String prefix) {
        for (String field : fields) {
            String key = prefix + field;
            if (!requestParameterMap.containsKey(key)) continue;
            String[] values = requestParameterMap.get(key);
            if (values == null || values.length < 1) continue;
            DataRuleConditionEnum conditionEnum = DataRuleConditionEnum.ORDER_BY_ASC;
            if (values[0].trim().equalsIgnoreCase("desc")) {
                conditionEnum = DataRuleConditionEnum.ORDER_BY_DESC;
            }
            if (enableHumpNaming()) {
                field = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(field);
            }
            constructorCondition(field, null, conditionEnum, enableHumpNaming());
        }
    }

    private void analysisEntityFieldCondition(String columnName, String[] values) {
        if (values[0] == null) return;
        DataRuleConditionEnum conditionEnum = DataRuleConditionEnum.getRuleCondition(values);
        String v = String.join(",", values);
        if (conditionEnum == null) conditionEnum = DataRuleConditionEnum.EQ;
        if (enableHumpNaming()) {
            columnName = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(columnName);
        }
        constructorCondition(columnName, v, conditionEnum, enableHumpNaming());
    }

    @Override
    public boolean enableHumpNaming() {
        return true;
    }

    /**
     * @param columnName       列名
     * @param columnValue      列值
     * @param conditionEnum    条件
     * @param enableHumpNaming 是否开启驼峰命名
     */
    protected void constructorCondition(String columnName,
                                        String columnValue,
                                        DataRuleConditionEnum conditionEnum,
                                        boolean enableHumpNaming) {

        String[] split;
        switch (conditionEnum) {
            case EQ://等于
                this.eq(columnName, columnValue);
                break;
            case GT://大于
                this.gt(columnName, columnValue);
                break;
            case GE://大于等于
                this.ge(columnName, columnValue);
                break;
            case LT://小于
                this.lt(columnName, columnValue);
                break;
            case LE://小于等于
                this.le(columnName, columnValue);
                break;
            case NE://不等于
                this.ne(columnName, columnValue);
                break;
            case BETWEEN://之间
                split = columnValue.split(",");
                if (split.length < 2) {
                    throw new RuntimeException("构建条件查询失败 between :参数：" + columnValue);
                }
                this.between(columnName, split[0], split[1]);
                break;
            case NOT_BETWEEN://不在之间
                split = columnValue.split(",");
                if (split.length < 2) {
                    throw new RuntimeException("构建条件查询失败 notBetween :参数：" + columnValue);
                }
                this.notBetween(columnName, split[0], split[1]);
                break;
            case LIKE://包含
                this.like(columnName, columnValue);
                break;
            case NOT_LIKE://不包含
                this.notLike(columnName, columnValue);
                break;
            case LIKE_LEFT://左模糊
                this.likeLeft(columnName, columnValue);
                break;
            case LIKE_RIGHT://右模糊
                this.likeRight(columnName, columnValue);
                break;
            case ORDER_BY_ASC:
                this.orderByAsc(columnName);
                break;
            case ORDER_BY_DESC:
                this.orderByDesc(columnName);
                break;
            case IN:
            case NOT_IN:
                final List<String> collect = Arrays.stream(columnValue.split(",")).distinct().collect(Collectors.toList());
                if (collect.isEmpty()) break;
                if (conditionEnum == DataRuleConditionEnum.IN) {
                    this.in(columnName, collect);
                } else {
                    this.notIn(columnName, collect);
                }
                break;
            case IS_NULL:
                this.isNull(columnName);
                break;
            case IS_NOT_NULL:
                this.isNotNull(columnName);
                break;
            case CUSTOM_SQL://自定义sql
                this.apply(StringUtils.isNotEmpty(columnValue), columnValue);
                break;

        }
    }


    /**
     * 检查规则合法性
     *
     * @param rules
     * @return
     */
    private boolean checkLegitimacy(StartBaseMenuDataRules rules) {
        List<String> fieldNames = getObjectFieldMap(entityClazz);
        return fieldNames.contains(rules.getRuleColumnName());
    }

    /**
     * 检查字段合法性 不合法的字段返回
     *
     * @param columnNames
     * @return
     */
    public List<String> checkLegitimacyColumns(List<String> columnNames) {
        List<String> source = getObjectFieldMap(entityClazz);
        List<String> tempList = new ArrayList<>();
        for (String columnName : columnNames) {
            if (source.contains(columnName)) continue;
            if (source.contains(com.baomidou.mybatisplus.core.toolkit.StringUtils.underlineToCamel(columnName))) {
                continue;
            }
            tempList.add(columnName);
        }
        return tempList;
    }

    public static <T> List<String> getObjectFieldMap(Class<T> clazz) {
        String className = clazz.getName();
        if (!objectCache.containsKey(className)) {
            analysisObject(clazz);
        }
        return objectCache.get(className);
    }

    private static <T> void analysisObject(Class<T> clazz) {
        String className = clazz.getName();
        synchronized (DataQueryConstructor.class) {
            if (objectCache.containsKey(className)) return;
            final List<Field> allFields = ClassTools.getAllFields(clazz);
            final List<String> collect = allFields.stream().map(Field::getName).distinct().collect(Collectors.toList());
            objectCache.put(className, collect);
        }
    }

}
